/*
 * Copyright (C) 2011 Olivier Pothin and contributors
 *
 * GNU General Public License v2.0
 *
 * This program is free software; you can redistribute it and/or
 * modify it under the terms of the GNU General Public License
 * as published by the Free Software Foundation; either version 2
 * of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 */

/**
 * 
 * @author Olivier Pothin
 *
 */
package opot.db;

import java.net.MalformedURLException;
import java.net.URL;
import java.util.HashMap;
import java.util.List;

import opot.dto.Stat;
import opot.dto.Word;
import opot.webservices.impl.DatabaseServiceImpl;
import android.content.ContentProvider;
import android.content.ContentUris;
import android.content.ContentValues;
import android.content.Context;
import android.content.UriMatcher;
import android.database.Cursor;
import android.database.SQLException;
import android.database.sqlite.SQLiteDatabase;
import android.database.sqlite.SQLiteOpenHelper;
import android.database.sqlite.SQLiteQueryBuilder;
import android.net.Uri;
import android.text.TextUtils;
import android.util.Log;

public class WordProvider extends ContentProvider {


	public static final String AUTHORITY = "fr.opot.chinois.Word";

	/** Database Helper **/
	private DatabaseHelper mOpenHelper;
	private static final String DATABASE_NAME = "languages";
	private static final int DATABASE_VERSION = 1;

	/** Uri Matcher **************************************/
	/** for words **/
	private static final int MOTS = 1;
	private static final int MOT_ID = 2;
	private static final int MOT_FR = 3;
	/** for stats */
	private static final int STATS = 4;
	private static final int STATS_ID = 4;
	/** The matcher **/
	private static final UriMatcher sUriMatcher;


	/** Les projections */
	private static HashMap<String, String> sWordProjection;
	private static HashMap<String, String> sFrenchWordProjection;
	private static HashMap<String, String> sStatsProjection;

	private static HashMap<String, String> sUpdateStatsProjection;


	/**
	 * A block that instantiates and sets static objects
	 */
	static {

		/*
		 * Creates and initializes the URI matcher
		 */
		// Create a new instance
		sUriMatcher = new UriMatcher(UriMatcher.NO_MATCH);

		// Add a pattern that routes URIs terminated with "notes" to a NOTES operation
		sUriMatcher.addURI(AUTHORITY, "words", MOTS);

		//add a pattern to fetch a word by it's id
		sUriMatcher.addURI(AUTHORITY, "words/#", MOT_ID);

		//add a pattern to fetch a word by french
		sUriMatcher.addURI(AUTHORITY, "words/fr/*",MOT_FR);

		//add pattern fo fetch all stats
		sUriMatcher.addURI(AUTHORITY, "stats", STATS);

		//add pattern fo fetch a quizz session by its ID
		sUriMatcher.addURI(AUTHORITY, "stats/#", STATS_ID);

		// Initialisation projection totale
		sWordProjection = new HashMap<String, String>();

		sWordProjection.put(Word.Words._ID,Word.Words._ID);
		sWordProjection.put(Word.Words.COLUMN_NAME_CHINESE,Word.Words.COLUMN_NAME_CHINESE);
		sWordProjection.put(Word.Words.COLUMN_NAME_PINYIN,Word.Words.COLUMN_NAME_PINYIN);
		sWordProjection.put(Word.Words.COLUMN_NAME_FRENCH,Word.Words.COLUMN_NAME_FRENCH);
		sWordProjection.put(Word.Words.COLUMN_NAME_CREATE_DATE,Word.Words.COLUMN_NAME_CREATE_DATE);
		sWordProjection.put(Word.Words.COLUMN_NAME_MODIFICATION_DATE,Word.Words.COLUMN_NAME_MODIFICATION_DATE);

		//initialise projection fr seulement
		sFrenchWordProjection = new HashMap<String, String>();
		sFrenchWordProjection.put(Word.Words._ID,Word.Words._ID);
		sFrenchWordProjection.put(Word.Words.COLUMN_NAME_FRENCH,Word.Words.COLUMN_NAME_FRENCH);

		//initialise projection for stats

		sStatsProjection = new HashMap<String, String>();
		sStatsProjection.put(Stat.Stats._ID, Stat.Stats._ID);
		sStatsProjection.put(Stat.Stats.COLUMN_NAME_DIFFICULTY, Stat.Stats.COLUMN_NAME_DIFFICULTY);
		sStatsProjection.put(Stat.Stats.COLUMN_NAME_START_DATE, Stat.Stats.COLUMN_NAME_START_DATE);
		sStatsProjection.put(Stat.Stats.COLUMN_NAME_END_DATE, Stat.Stats.COLUMN_NAME_END_DATE);
		sStatsProjection.put(Stat.Stats.COLUMN_NAME_NB_QUESTIONS, Stat.Stats.COLUMN_NAME_NB_QUESTIONS);
		sStatsProjection.put(Stat.Stats.COLUMN_NAME_NB_GOOD_ANSWER, Stat.Stats.COLUMN_NAME_NB_GOOD_ANSWER);
		sStatsProjection.put(Stat.Stats.COLUMN_NAME_NB_BAD_ANSWER, Stat.Stats.COLUMN_NAME_NB_BAD_ANSWER);

		sUpdateStatsProjection = new HashMap<String, String>();
		sUpdateStatsProjection.put(Stat.Stats._ID, Stat.Stats._ID);
		sUpdateStatsProjection.put(Stat.Stats.COLUMN_NAME_NB_GOOD_ANSWER, Stat.Stats.COLUMN_NAME_NB_GOOD_ANSWER);
		sUpdateStatsProjection.put(Stat.Stats.COLUMN_NAME_NB_BAD_ANSWER, Stat.Stats.COLUMN_NAME_NB_BAD_ANSWER);
		sUpdateStatsProjection.put(Stat.Stats.COLUMN_NAME_END_DATE, Stat.Stats.COLUMN_NAME_END_DATE);
	}

	private static class DatabaseHelper extends SQLiteOpenHelper {
		public DatabaseHelper(Context context) {
			super(context, DATABASE_NAME, null, DATABASE_VERSION);
		}

		@Override
		public void onCreate(SQLiteDatabase db) {
			//CREATE WORD TABLE
			db.execSQL("CREATE TABLE " + Word.Words.TABLE_NAME + " ("
					+ Word.Words._ID + " INTEGER PRIMARY KEY,"
					+ Word.Words.COLUMN_NAME_CHINESE + " TEXT collate nocase,"
					+ Word.Words.COLUMN_NAME_PINYIN + " TEXT collate nocase,"
					+ Word.Words.COLUMN_NAME_FRENCH + " TEXT collate nocase,"
					+  Word.Words.COLUMN_NAME_CREATE_DATE + " INTEGER,"
					+  Word.Words.COLUMN_NAME_MODIFICATION_DATE + " INTEGER"
					+ ");");
			//FILL WORD TABLE
			fillWordsFromUrl(db);

			//CREATE STATS TABLE
			db.execSQL("CREATE TABLE "+Stat.Stats.TABLE_NAME + " ( "
					+ Stat.Stats._ID + " INTEGER PRIMARY KEY,"	
					+ Stat.Stats.COLUMN_NAME_DIFFICULTY + " INTEGER,"
					+ Stat.Stats.COLUMN_NAME_START_DATE + " LONG,"
					+ Stat.Stats.COLUMN_NAME_END_DATE + " LONG,"
					+ Stat.Stats.COLUMN_NAME_NB_QUESTIONS + " INTEGER,"
					+ Stat.Stats.COLUMN_NAME_NB_GOOD_ANSWER + " INTEGER,"
					+ Stat.Stats.COLUMN_NAME_NB_BAD_ANSWER + " INTEGER"
					+ ");");
		}

		
		
		private void fillWordsFromUrl(SQLiteDatabase db) {
			try {
				DatabaseServiceImpl impl = new DatabaseServiceImpl();
				//FIXME put the url in ressources :)
				URL url = new URL("http://chinese-puzzle.googlecode.com/files/current_db2.xml");
				List <Word> liste = impl.getIntialisationQuery(1, url);
				
				for (Word w : liste) {
					db.execSQL("INSERT INTO words(zh_words,pin_words,fr_words) values ('"+w.zh+"','"+w.pin+"','"+w.fr+"');");
				}
				
			} catch (MalformedURLException e) {
				//TODO ajouter custom exception dans le cas ou le provider n'accede pas au web pour pouvoir chargere depuis le local par defaut 
				e.printStackTrace();
			}
		}
		
		@Override
		public void onUpgrade(SQLiteDatabase db, int oldVersion, int newVersion) {
			// TODO Auto-generated method stub

		}

	}


	@Override
	public int delete(Uri uri, String selection, String[] selectionArgs) {
		// TODO Auto-generated method stub
		return 0;
	}

	@Override
	public String getType(Uri uri) {
		// TODO Auto-generated method stub
		return null;
	}

	@Override
	public Uri insert(Uri uri, ContentValues initialValues) {
		switch (sUriMatcher.match(uri)) 
		{
		case MOTS: {	

			ContentValues values;

			if (initialValues != null) {
				values = new ContentValues(initialValues);
			} else {
				// Otherwise, create a new value map
				values = new ContentValues();
			}

			Long now = Long.valueOf(System.currentTimeMillis());

			// If the values map doesn't contain the creation date, sets the value to the current time.
			if (values.containsKey( Word.Words.COLUMN_NAME_CREATE_DATE) == false) {
				values.put(Word.Words.COLUMN_NAME_CREATE_DATE, now);
			}

			// If the values map doesn't contain the modification date, sets the value to the current
			// time.
			if (values.containsKey( Word.Words.COLUMN_NAME_MODIFICATION_DATE) == false) {
				values.put( Word.Words.COLUMN_NAME_MODIFICATION_DATE, now);
			}

			if(values.containsKey(Word.Words.COLUMN_NAME_CHINESE) == false)
				values.put(Word.Words.COLUMN_NAME_CHINESE, "Chinese_Value_missing");

			if(values.containsKey(Word.Words.COLUMN_NAME_FRENCH) == false)
				values.put(Word.Words.COLUMN_NAME_FRENCH, "French_Value_missing");

			if(values.containsKey(Word.Words.COLUMN_NAME_PINYIN) == false)
				values.put(Word.Words.COLUMN_NAME_PINYIN,"Pinyin_value_missing");

			SQLiteDatabase db = mOpenHelper.getWritableDatabase();

			long rowId = db.insert(Word.Words.TABLE_NAME,Word.Words.COLUMN_NAME_PINYIN,values);

			if (rowId > 0) {
				// Creates a URI with the note ID pattern and the new row ID appended to it.
				Uri noteUri = ContentUris.withAppendedId(Word.Words.CONTENT_ID_URI_BASE, rowId);

				// Notifies observers registered against this provider that the data changed.
				getContext().getContentResolver().notifyChange(noteUri, null);
				return noteUri;
			}
			throw new SQLException("Failed to insert row into " + uri);

		}
		case STATS: 
		{
			ContentValues values;

			if (initialValues != null) {
				values = new ContentValues(initialValues);
			} else {
				// Otherwise, create a new value map
				values = new ContentValues();
			}
	
			Long now = Long.valueOf(System.currentTimeMillis());

			// If the values map doesn't contain the creation date, sets the value to the current time.
			if (values.containsKey( Stat.Stats.COLUMN_NAME_START_DATE) == false) {
				values.put(Stat.Stats.COLUMN_NAME_START_DATE, now);
			}

			SQLiteDatabase db = mOpenHelper.getWritableDatabase();
			long rowId = db.insert(Stat.Stats.TABLE_NAME,Stat.Stats.COLUMN_NAME_DIFFICULTY,values);

			if (rowId > 0) {
				// Creates a URI with the note ID pattern and the new row ID appended to it.
				Uri noteUri = ContentUris.withAppendedId(Stat.Stats.CONTENT_ID_URI_BASE, rowId);

				// Notifies observers registered against this provider that the data changed.
				getContext().getContentResolver().notifyChange(noteUri, null);
				return noteUri;
			}
			throw new SQLException("Failed to insert row into " + uri);
		}		
		}
		throw new IllegalArgumentException("Uri doesn't match");
	}

	@Override
	public boolean onCreate() {
		mOpenHelper = new DatabaseHelper(getContext());
		return true;
	}

	@Override
	public Cursor query(Uri uri, String[] projection, String selection,
			String[] selectionArgs, String sortOrder) {

		SQLiteQueryBuilder qb = new SQLiteQueryBuilder();
		switch(sUriMatcher.match(uri)){

		case MOTS:
			qb.setTables(Word.Words.TABLE_NAME);
			qb.setProjectionMap(sWordProjection);
			break;
		case MOT_ID:
			qb.setTables(Word.Words.TABLE_NAME);
			qb.setProjectionMap(sWordProjection);
			qb.appendWhere(Word.Words._ID+
					"="+
					uri.getPathSegments().get(Word.Words.PATH_WORDS_ID_POSITION));
			break;

		case MOT_FR:
			qb.setTables(Word.Words.TABLE_NAME);
			qb.setProjectionMap(sFrenchWordProjection);
			qb.appendWhere(Word.Words.COLUMN_NAME_FRENCH+"="+uri.getPathSegments().get(Word.Words.PATH_WORDS_FRENCH_POSITION));
			break;

		case STATS:
			qb.setTables(Stat.Stats.TABLE_NAME);
			qb.setProjectionMap(sStatsProjection);
			break;

		default:
			throw new IllegalArgumentException("Unknown URI " + uri);
		}


		String orderBy;
		// If no sort order is specified, uses the default
		if (TextUtils.isEmpty(sortOrder)) {
			orderBy = "created asc";
		} else {
			// otherwise, uses the incoming sort order
			orderBy = sortOrder;
		}



		SQLiteDatabase db = mOpenHelper.getReadableDatabase();
		

		/*
		 * Performs the query. If no problems occur trying to read the database, then a Cursor
		 * object is returned; otherwise, the cursor variable contains null. If no records were
		 * selected, then the Cursor object is empty, and Cursor.getCount() returns 0.
		 */
		Cursor c = qb.query(
				db,            // The database to query
				projection,    // The columns to return from the query
				selection,     // The columns for the where clause
				selectionArgs, // The values for the where clause
				null,          // don't group the rows
				null,          // don't filter by row groups
				orderBy        // The sort order
		);

		// Tells the Cursor what URI to watch, so it knows when its source data changes
		c.setNotificationUri(getContext().getContentResolver(), uri);
		return c;
	}

	//TODO UPDATE SUR LES MOTS
	@Override
	public int update(Uri uri, ContentValues values, String selection,
			String[] selectionArgs) {

		if(values == null)
			throw new IllegalArgumentException("Missing value to update " + uri);

		switch(sUriMatcher.match(uri)){
		case STATS_ID:
			SQLiteDatabase db = mOpenHelper.getWritableDatabase();
			int rows = db.update(Stat.Stats.TABLE_NAME, values, Stat.Stats._ID+
					"="+
					uri.getPathSegments().get(Stat.Stats.PATH_STATS_ID_POSITION), null);
			Log.d("STATS","Number of stat modified should be 1 and is : "+rows);
			return rows;
		default:
			throw new IllegalArgumentException("Unknown URI " + uri);
		}
	}
}
